﻿using UnityEditor;
using System.IO;
using System.Collections.Generic;

//负责打包资源的类
public class BundleBuilder{
	//需要被打包的资源所在的总目录，相对路径，当前目录是Assets的上一级目录
	public const string BuildDirectory = "Assets/Res";
	//被打包出来的Bundle的输出目录，相对路径，当前目录是Assets的上一级目录
	private const string OutDirectory = "../BundleOutput";
	//打包时需要被忽略的文件后缀名
	private static List<string> IgnoreExtensions = new List<string>(new string[]{".meta"});

	[MenuItem("BundleBuilder/BuildAll")]
	private static void BuildAllBundle(){
		if (!Directory.Exists (BuildDirectory)) {
			return;
		}
		ResetOutPutBundleName ();
		var outPutDirectory = Path.Combine (OutDirectory, EditorUserBuildSettings.activeBuildTarget.ToString ());
		if (!Directory.Exists (outPutDirectory)) {
			Directory.CreateDirectory(outPutDirectory);
		}
		ClearDirectory (outPutDirectory);
		BuildPipeline.BuildAssetBundles(outPutDirectory, BuildAssetBundleOptions.None, EditorUserBuildSettings.activeBuildTarget);
		//BuildPipeline.BuildAssetBundles 是可以实现部分资源打包的，这对部分打包功能很重要。但是这里就懒得实现了
		RenameResourceMap (outPutDirectory, EditorUserBuildSettings.activeBuildTarget.ToString ());
		//打开指定的文件夹
		DirectoryInfo dic = new DirectoryInfo(outPutDirectory);
		System.Diagnostics.Process.Start("explorer.exe",dic.FullName);
	}

	//重置所有需要打包的文件的AssetBundleName属性，该属性如果存在，资源就会被打包到该属性指定的Bundle中，
	//所以应该保证项目中只有特定目录的资源有这个属性,否则会产生额外的bundle
	private static void ResetOutPutBundleName(){
		//遍历资源目录，根据每个资源文件的路径设置其输出的Bundle
		DirectoryInfo directory = new DirectoryInfo(BuildDirectory);
		FileInfo[] files = directory.GetFiles ("*", SearchOption.AllDirectories);
		foreach (var file in files) {
			if (IgnoreExtensions.Contains (file.Extension)) {
				continue;
			}
			var relativePath = file.FullName.Substring (file.FullName.IndexOf("Assets"));
			AssetImporter ai = AssetImporter.GetAtPath (relativePath);
			//每个资源的最近一级目录，将“\”或者“/”替换为"."后在加上.bundle后缀，就是该资源被打包到的bundle的名称，注意，不要在Res目录下直接添加资源文件
			//如果需要自定义打包粒度，可以自行修改此处（比如可以直接让每个资源生成一个bundle,不过这样的话bundle可能会很多）
			var resPath = relativePath.Substring (BuildDirectory.Length+1);
			resPath = resPath.Substring (0, resPath.LastIndexOf (Path.DirectorySeparatorChar));
			resPath = resPath.Replace (Path.DirectorySeparatorChar, '.');
			resPath = resPath + ".bundle";
			//注意：这里有一个坑，下面这句复制会强制将传入的值转化为全小写，bundlename 似乎只能是小写
			ai.assetBundleName = resPath;
		}
		AssetDatabase.Refresh ();
	}

	//清空目录
	private static void ClearDirectory(string path){
		if (!Directory.Exists (path)) {
			return;
		}
		Directory.Delete (path,true);
		Directory.CreateDirectory (path);
	}

	//打包后的相关处理
	private static void RenameResourceMap(string path,string fileName){
		//重命名特定的文件,bundle打完之后，unity会生成一个包含了所有bundle元数据的一个bundle，该bundle与目标文件夹同名。这里将其改名
		FileInfo file = new FileInfo(Path.Combine(path,fileName));
		string targetFile = Path.Combine (path, "ResourceMap");
		file.MoveTo (targetFile);
		//顺便把manifest文件也改个名字（其实这一步有没有都一样）
		file = new FileInfo (Path.Combine (path, fileName + ".manifest"));
		targetFile = Path.Combine (path, "ResourceMap.manifest");
		file.MoveTo (targetFile);
	} 
}
